#include "ff.h"
#include "db_wma_parse.h"
#include "oal_mem.h"

typedef struct
{//����WMA TAG �����ݸ�ʽ
	unsigned short title_len;
	unsigned short author_len;
	unsigned short copyright_len;
	unsigned short description_Len;
	unsigned short rating_len;
	//char *TaginfoBuf;
} asf_tagobj_t;

typedef struct
{
	unsigned char* str_buf;
	unsigned char str_len;
} tag_string_t;

#ifdef SUPPORT_REPLAYGAIN
#define EXTENDED_TYPE_CN             10
#else
#define EXTENDED_TYPE_CN             8
#endif

//Header object ��unicode
static const unsigned char asf_header_obj[16] =
{ 0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66, 0xcf, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c };
//Content Description Object ��unicode
static const unsigned char content_description_obj[16] =
{ 0x33, 0x26, 0xb2, 0x75, 0x8e, 0x66, 0xcf, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c };
//Extended Content Description Object��unicode
static const unsigned char extended_content_description_obj[16] =
{ 0x40, 0xA4, 0xD0, 0xD2, 0x07, 0xE3, 0xD2, 0x11, 0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50 };

//  WM/AlbumTitle ��unicode
//W M / A l b u m A r t i s t
static const unsigned char artist_descriptor_name[28] =
{ 0x57, 0x00, 0x4D, 0x00, 0x2F, 0x00, 0x41, 0x00, 0x6C, 0x00, 0x62, 0x00, 0x75, 0x00, 0x6D, 0x00, 0x41, 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00 };
static const unsigned char alb_descriptor_name[26] =
{ 0x57, 0x00, 0x4D, 0x00, 0x2F, 0x00, 0x41, 0x00, 0x6C, 0x00, 0x62, 0x00, 0x75, 0x00, 0x6D, 0x00, 0x54, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6C, 0x00, 0x65, 0x00 };
#ifdef SUPPORT_REPLAYGAIN
//  replaygain_track_gain ��unicode
static const unsigned char replaygain_track_descriptor_name[42] =
{ 0x72, 0x00, 0x65, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x67, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e,
		0x00, 0x5f, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x67, 0x00, 0x61,
		0x00, 0x69, 0x00, 0x6e, 0x00 };
//  replaygain_ablum_gain ��unicode
static const unsigned char replaygain_ablum_descriptor_name[42] =
{ 0x72, 0x00, 0x65, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x67, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e,
		0x00, 0x5f, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x62, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x67, 0x00, 0x61,
		0x00, 0x69, 0x00, 0x6e, 0x00 };
#endif

static const tag_string_t tag_string_info[EXTENDED_TYPE_CN] =
{
{ (unsigned char*)artist_descriptor_name, 28 },
{ 0x00, 0x00 },
{ (unsigned char*)alb_descriptor_name, 26 },
{ 0x00, 0x00 },// { (unsigned char*)genre_descriptor_name, 18 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },// { (unsigned char*)tracknum_descriptor_name, 30 },
{ 0x00, 0x00 },// { (unsigned char*)picture_descriptor_name, 20 },
{ 0x00, 0x00 }
#ifdef SUPPORT_REPLAYGAIN
,{ (uint8*)replaygain_track_descriptor_name, 42 },
{ (uint8*)replaygain_ablum_descriptor_name, 42 }
#endif
};

static int get_tag_type(unsigned char* buff)
{
	for (int i = 0; i < EXTENDED_TYPE_CN; i++)
	{
		if (tag_string_info[i].str_len)
		{
			if (0 == memcmp(buff, tag_string_info[i].str_buf, tag_string_info[i].str_len))
			{
				//i == 2, 3, 5, 6;
				return i;
			}
		}
	}
	return -1;
}

static int wma_content_object(FIL* f, ID3_INFO* id3_info, unsigned int objsize)
{
	int res = FR_OK;
	unsigned int br;
	unsigned char buff[512] = {0};
	asf_tagobj_t asf_tag;

	FSIZE_t end_offset = f_tell(f) + objsize;

	res = f_read(f, &asf_tag, sizeof(asf_tagobj_t), &br);
	if (res != FR_OK || br != sizeof(asf_tagobj_t))
	{
		return -1;
	}

	if (asf_tag.author_len)
	{
		f_lseek(f, f_tell(f) + asf_tag.title_len);
		res = f_read(f, buff, asf_tag.author_len, &br);
		if (res != FR_OK || br != asf_tag.author_len)
		{
			return -1;
		}

		if (asf_tag.author_len < ID3_ITEM_LEN)
		{
			memcpy(id3_info->artist.FrameCont, buff, asf_tag.author_len);
			id3_info->artist.len = asf_tag.author_len;
		}
		else
		{
			memcpy(id3_info->artist.FrameCont, buff, ID3_ITEM_LEN-1);
			id3_info->artist.len = ID3_ITEM_LEN-1;
		}
		id3_info->artist.FrameCont_Encode = 1;
	}

	f_lseek(f, end_offset);
	return res;
}

static int wma_excontent_object(FIL* f, ID3_INFO* id3_info, unsigned int objsize)
{
	int res = FR_OK;
	unsigned int br;
	unsigned short descriptors_count = 0;

	FSIZE_t end_offset = f_tell(f) + objsize;

	res = f_read(f, &descriptors_count, sizeof(short), &br);
	if (res != FR_OK || br != sizeof(short))
	{
		return -1;
	}

	for (int cnt = 0; cnt < descriptors_count; cnt++)
	{
		unsigned short taglen = 0;

		if (f_tell(f) >= end_offset)
		{
			break;
		}

		res = f_read(f, &taglen, sizeof(short), &br);
		if (res != FR_OK || br != sizeof(short))
		{
			return -1;
		}

		assert(taglen < 512);
		unsigned char buff[512] = { 0 };

		res = f_read(f, buff, taglen, &br);
		if (res != FR_OK || br != taglen)
		{
			return -1;
		}

		int tag_type = get_tag_type(buff);

		res = f_read(f, buff, 4, &br);
		if (res != FR_OK || br != 4)
		{
			return -1;
		}

		unsigned short value_type = *(unsigned short*)(&buff[0]);
		unsigned short value_leng = *(unsigned short*)(&buff[2]);

		if (tag_type != -1)
		{
			assert(value_type == 0);
			assert(value_leng < 512);
			res = f_read(f, buff, value_leng, &br);
			if (res != FR_OK || br != value_leng)
			{
				return -1;
			}
		}
		else
		{
			res = f_lseek(f, f_tell(f) + value_leng);
			if (res)	return res;
		}

		switch (tag_type)
		{
		case 0:	//������
			assert(value_leng < ID3_ITEM_LEN);
			memcpy(id3_info->artist.FrameCont, buff, value_leng);
			id3_info->artist.len = value_leng;
			id3_info->artist.FrameCont_Encode = 1;
			break;
		case 2:	//ר��
			assert(value_leng < ID3_ITEM_LEN);
			memcpy(id3_info->album.FrameCont, buff, value_leng);
			id3_info->album.len = value_leng;
			id3_info->album.FrameCont_Encode = 1;
			break;
		default:
			break;
		}
	}

	res = f_lseek(f, end_offset);
	return res;
}

int wma_parse(FIL* f, ID3_INFO* id3_info)
{
	int res = FR_OK;
	unsigned int br;
	unsigned char buff[64];
	unsigned int obj_total_size;
	unsigned int object_size;
	BOOL chk_content_flag = TRUE;
	BOOL chk_excontent_flag = TRUE;

	if (f_size(f) < 512)
		return -1;//-1=parse error

	res = f_lseek(f, 0);
	res += f_read(f, buff, 30, &br);
	if (res != FR_OK || br != 30)
	{
		return -1;
	}

	if (memcmp(buff, asf_header_obj, 16))
	{
		return -1;
	}

	obj_total_size = *(unsigned int*)(&buff[16]);
	
	while (f_tell(f) < obj_total_size)
	{
		if ((chk_content_flag == FALSE) && (chk_excontent_flag == FALSE))
		{
			res = FR_OK;
			break;
		}

		res += f_read(f, buff, 24, &br);
		if (res != FR_OK || br != 24)
		{
			return -1;
		}

		object_size = *(unsigned int*)(&buff[16]);
		object_size -= 24;

		if (chk_content_flag && (0 == memcmp(buff, content_description_obj, 16)))
		{
			res = wma_content_object(f, id3_info, object_size);
			if (res)	return res;
			chk_content_flag = FALSE;
		}
		else if (chk_excontent_flag && (0 == memcmp(buff, extended_content_description_obj, 16)))
		{
			res = wma_excontent_object(f, id3_info, object_size);
			if (res)	return res;
			chk_excontent_flag = FALSE;
		}
		else
		{
			res = f_lseek(f, f_tell(f) + object_size);
			if (res)	return -1;
		}
	}

	return res;
}

int db_wma_parse(const char * path, ID3_INFO * id3_info)//����0==����������-1=����������-2=no id3 info��others=fs error��
{
	int res = 0;
	FIL f;

	res = f_open(&f, path, (unsigned char)(FA_READ));
	if(res != FR_OK)
	{
		return -1;
	}

	if(res == FR_OK)
	{
		res = wma_parse(&f, id3_info);
	}

	f_close(&f);
	return res;
}

